﻿namespace System.Runtime.InteropServices
{
    /// <summary>
    /// This class contains methods that are mainly used to manage native memory.
    /// </summary>
    unsafe public static partial class NativeMemory
    {
        /// <summary>
        /// Allocates an aligned block of memory of the specified size and alignment, in bytes.
        /// T:System.OutOfMemoryException: Allocating byteCount of memory with alignment failed.
        /// T:System.ArgumentException: alignment is not a power of two.
        /// </summary>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <param name="alignment">The alignment, in bytes, of the block to allocate. This must be a power of 2.</param>
        /// <returns>A pointer to the allocated aligned block of memory.</returns>
        public static void* AlignedAlloc(nuint byteCount, nuint alignment)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64._aligned_malloc((ulong)byteCount, (ulong)alignment);
            }
            else
            {
                return NativeMethodsX86._aligned_malloc((uint)byteCount, (uint)alignment);
            }
        }

        /// <summary>
        /// Frees an aligned block of memory.
        /// </summary>
        /// <param name="ptr">A pointer to the aligned block of memory that should be freed.</param>
        public static void AlignedFree(void* ptr)
        {
            NativeMethods._aligned_free(ptr);
        }

        /// <summary>
        /// Reallocates an aligned block of memory of the specified size and alignment, in bytes.
        /// T:System.ArgumentException: alignment is not a power of two.
        /// T:System.OutOfMemoryException: Reallocating byteCount of memory with alignment failed.
        /// </summary>
        /// <param name="ptr">The previously allocated block of memory.</param>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <param name="alignment">The alignment, in bytes, of the block to allocate. This must be a power of 2.</param>
        /// <returns>A pointer to the reallocated aligned block of memory.</returns>
        public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment)
        {
            void* newPtr;
            if (Environment.Is64BitProcess)
            {
                if (ptr == null)
                {
                    // If the pointer is null, just allocate new memory
                    newPtr = NativeMethodsX64._aligned_malloc((ulong)byteCount, (ulong)alignment);
                }
                else
                {
                    // Allocate new aligned memory
                    newPtr = NativeMethodsX64._aligned_malloc((ulong)byteCount, (ulong)alignment);
                }

                if (newPtr == null)
                {
                    throw new OutOfMemoryException("Failed to allocate aligned memory.");
                }

                // Copy data from the old memory block to the new one
                // Note: You need to know the original size of the memory block to copy correctly
                // For simplicity, assume the original size is smaller than newSize
                NativeMethodsX64.memcpy(newPtr, ptr, (ulong)byteCount);

                // Free the old memory block
                NativeMethods._aligned_free(ptr);
            }
            else
            {
                if (ptr == null)
                {
                    // If the pointer is null, just allocate new memory
                    newPtr = NativeMethodsX86._aligned_malloc((uint)byteCount, (uint)alignment);
                }
                else
                {
                    // Allocate new aligned memory
                    newPtr = NativeMethodsX86._aligned_malloc((uint)byteCount, (uint)alignment);
                }

                if (newPtr == null)
                {
                    throw new OutOfMemoryException("Failed to allocate aligned memory.");
                }

                // Copy data from the old memory block to the new one
                // Note: You need to know the original size of the memory block to copy correctly
                // For simplicity, assume the original size is smaller than newSize
                NativeMethodsX86.memcpy(newPtr, ptr, (uint)byteCount);

                // Free the old memory block
                NativeMethods._aligned_free(ptr);
            }

            return newPtr;
        }












        /*
        /// <summary>
        /// 申请指定大小的内存
        /// </summary>
        /// <param name="byteCount">申请内存大小字节数</param>
        /// <returns>申请成功后指针,失败返回null</returns>
        public static void* Alloc(long byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.malloc((ulong)byteCount);
            }
            else
            {
                return NativeMethodsX86.malloc((uint)byteCount);
            }
        }

        /// <summary>
        /// 申请指定大小的内存
        /// </summary>
        /// <param name="byteCount">申请内存大小字节数</param>
        /// <returns>申请成功后指针,失败返回null</returns>
        public static void* Alloc(ulong byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.malloc((ulong)byteCount);
            }
            else
            {
                return NativeMethodsX86.malloc((uint)byteCount);
            }
        }
        */

        /// <summary>
        /// 申请指定大小的内存
        /// </summary>
        /// <param name="byteCount">申请内存大小字节数</param>
        /// <returns>申请成功后指针,失败返回null</returns>
        public static void* Alloc(nuint byteCount)
        {
            //return (void*)Marshal.AllocHGlobal(new IntPtr(byteCount));
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.malloc((ulong)byteCount);
            }
            else
            {
                return NativeMethodsX86.malloc((uint)byteCount);
            }
        }

        /// <summary>
        /// 申请指定大小的内存
        /// </summary>
        /// <param name="elementCount">元素个数</param>
        /// <param name="elementSize">元素大小</param>
        /// <returns>申请成功后指针,失败返回null</returns>
        public static void* Alloc(nuint elementCount, nuint elementSize)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.malloc((ulong)elementCount * (ulong)elementSize);
            }
            else
            {
                return NativeMethodsX86.malloc((uint)elementCount * (uint)elementSize);
            }
        }




        /// <summary>
        /// Allocates and zeroes a block of memory of the specified size, in bytes.
        /// T:System.OutOfMemoryException:  Allocating byteCount of memory failed.
        /// </summary>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <returns>A pointer to the allocated and zeroed block of memory.</returns>
        public static void* AllocZeroed(nuint byteCount)
        {
            void* ptr;
            if (Environment.Is64BitProcess)
            {
                // Allocate unmanaged memory
                ptr = NativeMethodsX64.malloc((ulong)byteCount);

                // Zero the allocated memory
                //NativeMethodsX64.memset(ptr, 0, (ulong)byteCount);
                NativeMethodsX64.ZeroMemory(ptr, (ulong)byteCount);//memset((Destination),0,(Length))
            }
            else
            {
                // Allocate unmanaged memory
                ptr = NativeMethodsX86.malloc((uint)byteCount);

                // Zero the allocated memory
                //NativeMethodsX86.memset(ptr, 0, (uint)byteCount);
                NativeMethodsX86.ZeroMemory(ptr, (uint)byteCount);
            }

            return ptr;
        }

        /// <summary>
        /// Allocates and zeroes a block of memory of the specified size, in elements.
        /// T:System.OutOfMemoryException:  Allocating elementCount * elementSize bytes of memory failed.
        /// </summary>
        /// <param name="elementCount">The count, in elements, of the block to allocate.</param>
        /// <param name="elementSize">The size, in bytes, of each element in the allocation.</param>
        /// <returns>A pointer to the allocated and zeroed block of memory.</returns>
        public static void* AllocZeroed(nuint elementCount, nuint elementSize)
        {
            return AllocZeroed(elementCount * elementSize);
        }




        /// <summary>
        /// 清空内存，全部设置为0
        /// </summary>
        /// <param name="ptr">要清空的指针</param>
        /// <param name="byteCount">清空的字节数</param>
        public static void Clear(void* ptr, nuint byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memset(ptr, 0, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memset(ptr, 0, (uint)byteCount);
            }
        }

        /*
        /// <summary>
        /// 清空内存，全部设置为0
        /// </summary>
        /// <param name="ptr">要清空的指针</param>
        /// <param name="byteCount">清空的字节数</param>
        public static void Clear(void* ptr, long byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memset(ptr, 0, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memset(ptr, 0, (uint)byteCount);
            }
        }
        */





        /// <summary>
        /// 
        /// </summary>
        /// <param name="source">原始指针</param>
        /// <param name="destination">目标指针</param>
        /// <param name="byteCount">移动字节数</param>
        /// <returns></returns>
        public static void* Memmove(void* source, void* destination, uint byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.memmove(destination, source, (ulong)byteCount);
            }
            else
            {
                return NativeMethodsX86.memmove(destination, source, (uint)byteCount);
            }
        }

        /// <summary>
        /// 拷贝内存
        /// </summary>
        /// <param name="source">原始指针</param>
        /// <param name="destination">目标指针</param>
        /// <param name="byteCount">拷贝字节数</param>
        /// <param name="copyUseMove">复制使用移动标识[true;复制使用memmove;false:复制使用memcpy]</param>
        public static void Copy(void* source, void* destination, nuint byteCount, bool copyUseMove = true)
        {
            if (copyUseMove)
            {
                if (Environment.Is64BitProcess)
                {
                    NativeMethodsX64.memmove(destination, source, (ulong)byteCount);
                }
                else
                {
                    NativeMethodsX86.memmove(destination, source, (uint)byteCount);
                }
            }
            else
            {
                if (Environment.Is64BitProcess)
                {
                    NativeMethodsX64.memcpy(destination, source, (ulong)byteCount);
                }
                else
                {
                    NativeMethodsX86.memcpy(destination, source, (uint)byteCount);
                }
            }

        }

        /*
        /// <summary>
        /// 拷贝内存
        /// </summary>
        /// <param name="source">原始指针</param>
        /// <param name="destination">目标指针</param>
        /// <param name="byteCount">拷贝字节数</param>
        public static void Copy(void* source, void* destination, long byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memcpy(destination, source, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memcpy(destination, source, (uint)byteCount);
            }
        }

        /// <summary>
        /// 拷贝内存
        /// </summary>
        /// <param name="source">原始指针</param>
        /// <param name="destination">目标指针</param>
        /// <param name="byteCount">拷贝字节数</param>
        public static void Copy(void* source, void* destination, ulong byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memcpy(destination, source, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memcpy(destination, source, (uint)byteCount);
            }
        }
        */






        /// <summary>
        /// 填充数据
        /// </summary>
        /// <param name="ptr">要填充的指针</param>
        /// <param name="byteCount">填充的字节数</param>
        /// <param name="value">填充的值</param>
        public static void Fill(void* ptr, nuint byteCount, byte value)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memset(ptr, value, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memset(ptr, value, (uint)byteCount);
            }
        }

        /*
        /// <summary>
        /// 填充数据
        /// </summary>
        /// <param name="ptr">要填充的指针</param>
        /// <param name="byteCount">填充的字节数</param>
        /// <param name="value">填充的值</param>
        public static void Fill(void* ptr, long byteCount, byte value)
        {
            if (Environment.Is64BitProcess)
            {
                NativeMethodsX64.memset(ptr, value, (ulong)byteCount);
            }
            else
            {
                NativeMethodsX86.memset(ptr, value, (uint)byteCount);
            }
        }
        */






        /// <summary>
        /// 释放指针
        /// </summary>
        /// <param name="ptr">要释放的指针</param>
        public static void Free(void* ptr)
        {
            //Marshal.FreeHGlobal((IntPtr)ptr);
            NativeMethods.free(ptr);
        }




        /// <summary>
        /// 扩展已有指针指向内存大小
        /// </summary>
        /// <param name="ptr">已有指针指</param>
        /// <param name="byteCount">扩展后的内存大小字节数</param>
        /// <returns></returns>
        public static void* Realloc(void* ptr, nuint byteCount)
        {
            //return (void*)Marshal.ReAllocHGlobal((IntPtr)ptr, new IntPtr(byteCount));
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.realloc(ptr, (ulong)byteCount);
            }
            else
            {
                return NativeMethodsX86.realloc(ptr, (uint)byteCount);
            }
        }

        /*
        /// <summary>
        /// 扩展已有指针指向内存大小
        /// </summary>
        /// <param name="ptr">已有指针指</param>
        /// <param name="byteCount">扩展后的内存大小字节数</param>
        /// <returns></returns>
        public static void* Realloc(void* ptr, ulong byteCount)
        {
            if (Environment.Is64BitProcess)
            {
                return NativeMethodsX64.realloc(ptr, byteCount);
            }
            else
            {
                return NativeMethodsX86.realloc(ptr, (uint)byteCount);
            }
        }
        */






        private class NativeMethods
        {
            [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
            internal static extern void _aligned_free(void* ptr);

            [DllImport("msvcrt.dll", EntryPoint = "free", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void free(void* ptr);
        }

        private class NativeMethodsX64
        {
            [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
            internal static extern void* _aligned_malloc(ulong size, ulong alignment);

            [DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
            internal static extern void ZeroMemory(void* dest, ulong size);

            [DllImport("msvcrt.dll", EntryPoint = "malloc", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* malloc(ulong size);

            [DllImport("msvcrt.dll", EntryPoint = "realloc", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* realloc(void* dest, ulong size);

            [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* memcpy(void* dest, void* src, ulong count);

            /// <summary>
            /// 
            /// </summary>
            /// <param name="ptr">要填充的指针</param>
            /// <param name="value">填充的值</param>
            /// <param name="byteCount">填充的字节数</param>
            /// <returns></returns>
            [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* memset(void* ptr, int value, ulong byteCount);

            /// <summary>
            /// 
            /// </summary>
            /// <param name="dest"></param>
            /// <param name="src"></param>
            /// <param name="byteCount"></param>
            /// <returns></returns>
            [DllImport("msvcrt.dll", EntryPoint = "memmove", CallingConvention = CallingConvention.Cdecl)]
            internal static extern void* memmove(void* dest, void* src, ulong byteCount);
        }

        private class NativeMethodsX86
        {
            [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
            internal static extern void* _aligned_malloc(uint size, uint alignment);


            [DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
            internal static extern void ZeroMemory(void* dest, uint size);//memset((Destination),0,(Length))

            [DllImport("msvcrt.dll", EntryPoint = "malloc", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* malloc(uint size);

            [DllImport("msvcrt.dll", EntryPoint = "realloc", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* realloc(void* dest, uint size);

            [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* memcpy(void* dest, void* src, uint count);

            /// <summary>
            /// 
            /// </summary>
            /// <param name="ptr">要填充的指针</param>
            /// <param name="value">填充的值</param>
            /// <param name="byteCount">填充的字节数</param>
            /// <returns></returns>
            [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
            internal static extern void* memset(void* ptr, int value, uint byteCount);

            /// <summary>
            /// 
            /// </summary>
            /// <param name="dest"></param>
            /// <param name="src"></param>
            /// <param name="byteCount"></param>
            /// <returns></returns>
            [DllImport("msvcrt.dll", EntryPoint = "memmove", CallingConvention = CallingConvention.Cdecl)]
            internal static extern void* memmove(void* dest, void* src, uint byteCount);
        }

    }

}
